/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package BL;

import BE.*;
//import Metodos.TwoOpt.*;
import java.util.List;
//import java.io.*;
import java.util.ArrayList;
import BL.BLParametrosDespacho;
//import java.math.*;
/**
 *
 * @author Elessar
 */
public class BLGraspDespacho {
    //Variables procesamiento
    //private int totalNodos;
    //private int cantObligatorios;
   // private ArrayList<Integer> nodosObligatorios;
    private ArrayList<BELineaDespacho> lstLineaDespacho;
    private ArrayList<BENodoDespacho> grafo = null;
    private int cantIteraciones;
    private double alpha;
    private BENodoDespacho nodoActual = null;
    private int idAnterior;
    //private BENodoDespacho ultObligatorio = null;
    
    //Variable respuesta
    private ArrayList<BENodoDespacho> rptaGrasp = null;
    private ArrayList<BENodoDespacho> Optimo = null;
    private double tamañoOptimo = 0;
    
    private ArrayList<BENodoDespacho> RCL = new ArrayList <BENodoDespacho>();
    private ArrayList<Integer> listaRecorridos = new ArrayList<Integer>();

    //private List<Integer> bloqueados = new ArrayList<Integer>();
    //private int[] contador;
    //private int regresoInicio = 0;
    private int cantTotal = 0;
    private ArrayList<BEProductoOblig> obligatorios;
    private double distMax;
    private BLParametrosDespacho blDespacho;

    
    //Constructor GRASP
    public BLGraspDespacho (ArrayList<BEProductoOblig> lstObligatorios, ArrayList<BENodoDespacho> grafo)
    {
        //this.cantObligatorios = cantObligatorios;
        //this.nodosObligatorios = nodosObligatorios;
        //this.totalNodos = totalNodos;
        this.obligatorios = lstObligatorios;
        for (int i=0; i<lstObligatorios.size(); i++){
            cantTotal+=lstObligatorios.get(i).getCantidad();
        }
        this.grafo = grafo;
//        this.cantIteraciones = 0;
//        this.alpha = 0.5;
        //Nodo actual se inicializa como la entrada del almacen
        this.nodoActual = grafo.get(0);
        
        BLAlmacen blAlmacen = new BLAlmacen();
        this.distMax = Math.sqrt(Math.pow(1*1.0-blAlmacen.getAncho()*1.0,2.0)
                +Math.pow(1*1.0-blAlmacen.getLargo()*1.0,2.0)
                +Math.pow(1*1.0-4*1.0,2.0));
        //System.out.println(distMax);
        //contador = new int[totalNodos];
        blDespacho = new BLParametrosDespacho();
        blDespacho.cargar();
    }
    
        
    public BENodoDespacho dameNodoRCL(BENodoDespacho nodoActual)
    {
        List<BENodoDespacho> nodos = getVecinos(nodoActual);
        //List<BENodoDespacho> nodos = new ArrayList<BENodoDespacho>();
        double mejorRuta = 800;
        double peorRuta = 0;
        int indiceRCL = 0;
        int index;

        //limpiando nodos bloqueados
//        for (int j = 0;j<nodosVecinos.size();j++)
//            if (//!bloqueados.contains(nodosVecinos.get(j).getIdNodo()) && 
//                    !listaRecorridos.contains(nodosVecinos.get(j).getIdNodo())){
//                //if (nodos2.get(j).getIdNodo()!=this.idAnterior && this.regresoInicio<1)
////                if (ultObligatorio!=null && !(nodos.contains(nodosVecinos.get(j)))){
////                    if (ultObligatorio.getX()<nodoActual.getX() && nodosVecinos.get(j).getX()<nodoActual.getX())
////                        nodos.add(nodosVecinos.get(j));
////                    if (ultObligatorio.getY()<nodoActual.getY() && nodosVecinos.get(j).getY()<nodoActual.getY())
////                        nodos.add(nodosVecinos.get(j));
////                    if (ultObligatorio.getX()>nodoActual.getX() && nodosVecinos.get(j).getX()>nodoActual.getX())
////                        nodos.add(nodosVecinos.get(j));
////                    if (ultObligatorio.getY()>nodoActual.getY() && nodosVecinos.get(j).getY()>nodoActual.getY())
////                        nodos.add(nodosVecinos.get(j));
////                }    
////                else 
//                    nodos.add(nodosVecinos.get(j));
//                    //nodos.add(grafo.get(nodosVecinos.get(j).getIdNodo()-1));
//            }

//        if (nodos.isEmpty())
//            System.out.println("El nodo " + nodoActual.getIdNodo() + " no tiene vecinos habilitados");
//        if (nodos.isEmpty())
//            for (int j = 0;j<nodosVecinos.size();j++)
//                nodos.add(nodosVecinos.get(j));

        double auxDist = 0;
        //Obtener "menor" distancia
        for (int i = 0;i<nodos.size();i++)
        {
            if (nodos.get(i).getIdNodo() != idAnterior){            
                auxDist = nodos.get(i).getDist();
                if (mejorRuta > auxDist && auxDist != 0)
                    mejorRuta = auxDist;
            }
        }
            
        //Obtener "mayor" distancia
        for (int i = 0;i<nodos.size();i++)
        {   
            if (nodos.get(i).getIdNodo()!=idAnterior){
                auxDist = nodos.get(i).getDist();
                if (peorRuta < auxDist && auxDist != 0)
                    peorRuta = auxDist;
            }
        }
        
        //Obtener lista RCL
        //int[] indicesRCL = new int[nodoActual.getVecinos().size()]; //peor de los casos
        //tenga los mismo que vecinos
        RCL.clear();
        for (int i = 0;i<nodos.size();i++)
        {
            auxDist = nodos.get(i).getDist();
            if (auxDist >= mejorRuta && auxDist <= this.alpha *
                                       (peorRuta - mejorRuta) + mejorRuta)
                RCL.add(nodos.get(i));
        }

        //Escojer un indice aleatoriamente
        indiceRCL = aleatorio(RCL.size());
              
        //Devolvemos el hijo que corresponda a ese indice
        if (RCL.size()>0)            
            return RCL.get(indiceRCL);
        else { 
            System.out.println("ERROR : " +nodoActual.getIdNodo()+" Recorrido:"+listaRecorridos.size());
            for (int j=0; j<nodos.size(); j++){
                index = listaRecorridos.indexOf(nodos.get(j).getIdNodo());
                if (index>-1 && nodos.get(j).getIdNodo()!=idAnterior)
                    listaRecorridos.remove(index);
            }
//                if (listaRecorridos.contains(nodosVecinos.get(j).getIdNodo()))
//                    listaRecorridos.remove(listaRecorridos.indexOf(nodosVecinos.get(j).getIdNodo()));
            //listaRecorridos.clear();
            //return nodosVecinos.get(0);
            //return dameNodoRCL(nodoActual);
            return null;
        }
    }
    
    public ArrayList<BENodoDespacho> getVecinos(BENodoDespacho nodoActual){
        ArrayList<BENodoDespacho> vecinos = new ArrayList<BENodoDespacho>();
        double pesoDist = blDespacho.getBeDespacho().getPesoDistancia();
        double pesoVenc = blDespacho.getBeDespacho().getPesoVencimiento();
        for (int i=1; i<grafo.size(); i++){
            if (grafo.get(i).getIdNodo() != nodoActual.getIdNodo() &&
                    !listaRecorridos.contains(grafo.get(i).getIdNodo())){
                //BENodoDespacho nodo = new BENodoDespacho(grafo.get(i));
                vecinos.add(grafo.get(i));
                double dist = calcularDistancia(nodoActual, grafo.get(i))*pesoDist+ grafo.get(i).getPeso()*pesoVenc;
                //dist = 1-(distMax-dist)/distMax;
                vecinos.get(vecinos.size()-1).setDist(dist);
            }
        }
        return vecinos;
    }
    
    public double calcularDistancia (BENodoDespacho nodo1, BENodoDespacho nodo2){
        double dist = Math.sqrt(Math.pow(nodo1.getX()*1.0-nodo2.getX()*1.0,2.0)
                +Math.pow(nodo1.getY()*1.0-nodo2.getY()*1.0,2.0)
                +Math.pow(nodo1.getZ()*1.0-nodo2.getZ()*1.0,2.0));
        //return dist + nodo2.getPeso();
        return 1-(distMax-dist)/distMax;        
    }

//    public boolean esObligatorio(String[] obligatorios, BENodo nodo)
//    {
//        boolean esta = false;
//        String nombre = ""+nodo.getIdNodo();
//
//        for (int i=0;i<obligatorios.length;i++)
//            if (nombre.equals(obligatorios[i]))
//            {
//                esta = true;
//                break;
//            }
//        return esta;
//    }

    public double getTotalDistPeso(ArrayList<BENodoDespacho> nodos)
    {
//        int tamano = 0;
//        for(int i = 0;i<nodos.size();i++)
//            tamano += nodos.get(i).getDist();
//        return tamano;
        double dist=0;
        for (int i=0; i<nodos.size()-1; i++){
            dist+=calcularDistancia(nodos.get(i),nodos.get(i+1))+nodos.get(i+1).getPeso();
        }
        return dist;
    }
    

    public BENodoDespacho cambiaNodoActual(int id)
    {
        BENodoDespacho devolver = null;
        //implementar busqueda binaria!
        for(int i=0;i<this.grafo.size();i++)
            if (id == this.grafo.get(i).getIdNodo())
            {
                devolver = this.grafo.get(i);
                break;
            }
        return devolver;
    }

    public static int aleatorio(int max){
		return (int)(Math.random()*(max-0))+0;
    }

//    public ArrayList<BEProductoOblig> getProductosObligatorios(){
//        ArrayList<BEProductoOblig> obligatorios = new ArrayList<BEProductoOblig>();
//        for (int i=0; i<this.lstLineaDespacho.size(); i++){
//            int idproducto = lstLineaDespacho.get(i).getObjPXT().getIdProducto();
//            int idtipopaleta = lstLineaDespacho.get(i).getObjPXT().getIdTipoPallet();
//            int cantidad = lstLineaDespacho.get(i).getCantidadPallets();
//            obligatorios.add(new BEProductoOblig(idproducto,idtipopaleta,cantidad));
//            this.cantTotal+=cantidad;
//        }
//        return obligatorios;
//    }
    
    public ArrayList<BENodoDespacho> run(double alp, int ni)
    {
        //Inicializar los parametros propios del GRASP
        //inicializarParametros();
        this.alpha=alp;
        this.cantIteraciones=ni;
        //Fase de Construccion
 
        //Inicio de la iteracion
        
        BENodoDespacho mejorRCL = null;
        ArrayList<BENodoDespacho> auxOptimo = null;
        //ArrayList<BEProductoOblig> obligatorios = null;
        for (int i=0;i<this.cantIteraciones;i++)
        {    
            //System.out.println(" iteracion "+i+": ");
            listaRecorridos.clear();
            auxOptimo = new ArrayList<BENodoDespacho>();
//            BENodoDespacho partida = new BENodoDespacho(0,0,0,1,1,1,0);
            BENodoDespacho partida = this.grafo.get(0);
//            partida.setIdNodo(this.grafo.get(0).getIdNodo());
//            partida.setDist(0);
            auxOptimo.add(partida);
            idAnterior=-1;  
            int idNodo = -1;
            
            ArrayList<BEProductoOblig> auxObligatorios = new ArrayList<BEProductoOblig>();
            for (int k=0; k<obligatorios.size(); k++){
                BEProductoOblig auxProd = new BEProductoOblig(obligatorios.get(k).getIdproducto(),
                        obligatorios.get(k).getIdtipopaleta(),obligatorios.get(k).getCantidad());
                auxObligatorios.add(auxProd);
            }
            //auxObligatorios.addAll(this.obligatorios);
            int auxCantObligatorio = 0;
            this.nodoActual = this.grafo.get(0);

            //bloqueados.clear();
//            for (int k=0;k<contador.length;k++)
//                contador[k] = 0;
            //System.out.println("Iteracion " + i);
            while (auxCantObligatorio != this.cantTotal)
            {
                
                mejorRCL = dameNodoRCL(this.nodoActual);
                while (mejorRCL==null) mejorRCL = dameNodoRCL(this.nodoActual);
                //System.out.print(" 1:"+mejorRCL.getIdNodo()+", ");

                //verficando el bloqueo de nodos
//                contador[mejorRCL.getIdNodo()-1] =contador[mejorRCL.getIdNodo()-1] +1;
//                if (contador[mejorRCL.getIdNodo()-1] == 50)
//                {
//                    System.out.println("--Bloqueado = " + mejorRCL.getIdNodo());
//                    bloqueados.add(mejorRCL.getIdNodo());
//                }

                //nodoActual.getIdNodoPadre();
                
                for (int j=0; j<auxObligatorios.size(); j++){
                    if (auxObligatorios.get(j).getIdproducto()==mejorRCL.getIdproducto() &&
                            auxObligatorios.get(j).getIdtipopaleta()==mejorRCL.getIdtipopaleta()){
                        
                        if (auxObligatorios.get(j).getCantidad()>0){
                            //auxObligatorios.get(j).setCantidad(auxObligatorios.get(j).getCantidad()-mejorRCL.getCantidad());
                            auxObligatorios.get(j).setCantidad(auxObligatorios.get(j).getCantidad()-1);
                            System.out.println("--- prod:"+auxObligatorios.get(j).getIdproducto()
                                    +" tipop:"+auxObligatorios.get(j).getIdtipopaleta()
                                    +" | cant:"+auxObligatorios.get(j).getCantidad()
                                    +" | pallet:"+mejorRCL.getIdpallet());
                            //auxCantObligatorio+=mejorRCL.getCantidad();
                            auxCantObligatorio++;
                            
                            auxOptimo.add(mejorRCL);  
                            idNodo = mejorRCL.getIdNodo();
                            idAnterior=nodoActual.getIdNodo();
                            listaRecorridos.add(idAnterior);                    
                            this.nodoActual = cambiaNodoActual(idNodo);
                            
                        }
                        else {
                            listaRecorridos.add(mejorRCL.getIdNodo());
                        }
                        break;
                    }
                }
//                auxOptimo.add(mejorRCL);  
//                idNodo = mejorRCL.getIdNodo();
//                int index=obligatorios.indexOf(idNodo);
//                if (index>-1){
//                    auxCantObligatorio++;
//                    obligatorios.remove(index);
////                    if (obligatorios.size()==1){
////                        ultObligatorio = grafo.get(obligatorios.get(0)-1);
////                    }                        
//                }
//                if (obligatorios.contains(idNodo))
//                {
//                    auxCantObligatorio++;
//                    obligatorios.remove(obligatorios.indexOf(idNodo));
//                }

                //Regreso al punto inicial
//                if (auxCantObligatorio == this.cantTotal)
//                {
//                    this.regresoInicio++;
//                    if (this.regresoInicio == 1)
//                    {
//                        auxCantObligatorio--;
//                        obligatorios.clear();
//                        obligatorios.add(1);
//                        ultObligatorio = grafo.get(obligatorios.get(0)-1);
//                        //this.bloqueados.clear();
//                        this.listaRecorridos.clear();
////                        for (int k=1;k<this.contador.length;k++)
////                            this.contador[k]=0;
//                    }
//                }

//                idAnterior=nodoActual.getIdNodo();
//                //if (!listaRecorridos.contains(idAnterior))
//                listaRecorridos.add(idAnterior);                    
//                this.nodoActual = cambiaNodoActual(idNodo);
                    //System.out.println(idAnterior+"- "+nodoActual.getIdNodo());
            }
            //System.out.println(" recorrido: ");
            //for (int k=0; k< listaRecorridos.size(); k++) 
            //    System.out.print(" "+listaRecorridos.get(k)+",");
            listaRecorridos.clear();
            if (this.Optimo == null) {
                this.Optimo = auxOptimo;
                this.tamañoOptimo = getTotalDistPeso(auxOptimo);
                System.out.println("i"+i+") "+tamañoOptimo);
            }
            else
            { 
                double auxTamano = getTotalDistPeso(auxOptimo);
                System.out.println("i"+i+") "+auxTamano);
                if ( auxTamano <this.tamañoOptimo){
                    this.Optimo = auxOptimo;
                    this.tamañoOptimo = auxTamano;
                }
            }
            mejorRCL = null;
            //this.regresoInicio=0;
            //auxOptimo = null;
        }
        
        this.rptaGrasp = this.Optimo;
        
        //Local search 2-opt        
        //return TwoOpt.TwoOpt (this.grafo, this.rptaGrasp);

        return this.rptaGrasp;
    }    
    
}
